#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
using namespace std;
#define MAX_STUDENTS_NUM 200
#define DEL_IDENTIFY_NUM 99999
#define COURSES_NUM 3

struct stuInfo
{
	int  identify_num;
	char Name[64];
	char StuID[16];
	char Gender[8];
	char TelNum[16];
	int  scores[16];
	int  StuID_int;
	// Chn, Math, Eng
};
stuInfo stuData[200];
char courses_tag[16][16]={"Chn","Math","Eng"};

void show_courses_name()
{
	printf("# Courses identify No.: \n");
	for (int i=0;i<COURSES_NUM;i++)
	{
		printf("  %d. %s\n",i+1,courses_tag[i]);
	}
	return ;
}
void view_student(int i) //i is the place at struct StuData
{
	printf("%2d. %-8s %-20s %-4s %-16s ",i+1,
		stuData[i].StuID, stuData[i].Name, stuData[i].Gender,stuData[i].TelNum);
	for (int j=0;j<COURSES_NUM;j++) printf("%3d ",stuData[i].scores[j]);
	printf("\n");
}
struct KeyInfo
{
	//char KeySection[100];
	char KeyName[128];
	//int  KeyValue_int;
	char KeyValue[128];
};
KeyInfo Keys[30]; //kind of a buffer

char* str2num(char* a, int *num) //start point, return num;
{
	int t=0,tmp[10],p=1,if_minus=0;
	char *k=a;
	while (*k<'0'||*k>'9')
	{
		if(*k=='\0') return k;
		k++;
	}
	if(*(k-1)=='-') if_minus=1;
	while (*k>='0' && *k<='9') tmp[t++]=*(k++)-'0';
	t=t-1;
	*num=0;
	while(t>=0)
	{
		*num+=tmp[t]*p;
		t--;
		p*=10;
	}
	if(if_minus==1) *num=-*num;
	return k; //return final point
}

void trim(char* strIn, char* strOut) // support in-place opreation
{
	char *a=strIn, *b;
	while (*a == ' '||*a == '\n' ) a++; // ignore spaces at the beginning
	b = strIn + strlen(strIn) - 1; // get pointer pointing at the end of the line
	while (*b == ' '||*b == '\n' ) b--; // ignore spaces at the end
	while (a<=b) *strOut++ = *a++; // transplace
	*strOut='\0';
}

int GetInt(const char *tgtName,int entriesNum, int nDefault = 0)
{
	for (int i=0; i<entriesNum; i++)
	{
		if(strcmp(tgtName,Keys[i].KeyName)==0)
			str2num(Keys[i].KeyValue, &nDefault);
	}
	return nDefault;
}
char GetChar(const char *tgtName,int entriesNum, char nDefault = '\0')
{
	for (int i=0; i<entriesNum; i++)
	{
		if(strcmp(tgtName,Keys[i].KeyName)==0)
			return Keys[i].KeyValue[1];
	}
	return nDefault;
}
void GetStr(const char *tgtName, int entriesNum, char* dstStr, const char* nDefault = " ")
{
	for (int i=0; i<entriesNum; i++)
	{
		if(strcmp(tgtName,Keys[i].KeyName)==0)
		{
			char* tgt= Keys[i].KeyValue + 1;
			while(*tgt!='"') *dstStr++=*tgt++;
			*dstStr ='\0';
			return;
		}
	}
	strcpy(dstStr, nDefault);
}
void empty_entries(int now_stu, int now_sec,int k)
{
	stuData[now_stu].identify_num = now_sec;
	stuData[now_stu].scores[0] = GetInt("Chn",k,0);
	stuData[now_stu].scores[1] = GetInt("Math",k,0);
	stuData[now_stu].scores[2] = GetInt("Eng",k,0);
	GetStr("Gender",k,stuData[now_stu].Gender," ");
	GetStr("ID",k,stuData[now_stu].StuID ," ");
	GetStr("Name",k,stuData[now_stu].Name," ");
	GetStr("Tel",k,stuData[now_stu].TelNum," ");
	stuData[now_stu].StuID_int = atoi(stuData[now_stu].StuID);
	/* int i=now_stu; view_student(i);*/
	//printf ("- %d entries for Section [%05d] has been transferred to stuData. \n",k,now_sec);
}
int load_stuData(const char *libFile = "stulib.txt")
{
	memset(stuData, 0x00, sizeof(stuData));

	FILE *fp = fopen (libFile,"r");
	if (fp==NULL)
	{
		printf("# Can't open library file \"%s\". \n",libFile);
		return 0;
	}
	else printf("# Successfully open library file \"%s\". \n",libFile);

	char c, now_section[16]= {0}, tmp[128],*t;
	int now_sec=0,tot_students=0,k=0;

	while((c=fgetc(fp))!=EOF)
	{
		while( c== ' '||c=='\n' ) c=fgetc(fp); 
		if ( c==EOF ) break;

		if (c=='[') // read section identifier;
		{
			if (k!=0) //meaning there're entries stored in "Keys" for the previous Sec.
			{
				empty_entries(tot_students,now_sec,k);
				tot_students++;
				k=0;
			}
			//update now_sec (int);
			t=tmp;
			while(c!='\n') 
			{
				*t++=c;
				fscanf(fp,"%c",&c);
			}
			*t = '\0';
			strcpy(now_section,tmp);
			str2num(now_section,&now_sec);
			//printf("- Section Updated: Now section: [%05d] \n",now_sec);
			//# now_sec(int) updated;
			continue; 
		}
		
		t = tmp;
		while( c!='=' && c!= ':'&& c!='\n') //read entry's name;
		{
			*t++ = c;
			fscanf(fp,"%c",&c);
		}
		*t = '\0';
		trim(tmp,tmp); //dispose spaces
		strcpy(Keys[k].KeyName,tmp);
		
		t = tmp;
		fscanf(fp,"%c",&c);
		while( c == ' ' || c == '\n' ) fscanf(fp,"%c",&c); //looking for the left quotation mark
		//fscanf(fp,"%c",&c);
		while( c != ';' && c != '\n' && c!= EOF) //read entry's value until the next mark
		{
			*t++=c;
			fscanf(fp,"%c",&c);
		}
		*t = '\0';
		trim(tmp,tmp);
		strcpy(Keys[k].KeyValue,tmp);
		//printf("- Now Section %d, %d entries has been readed. ",now_sec,k+1);
		//printf("Name = %s; Value = %s \n",Keys[k].KeyName,Keys[k].KeyValue);
		k++; //finish reading an entry, cnt++
	}
	if(k!=0) 
	{
		empty_entries(tot_students,now_sec,k);
		tot_students++;
		k=0;
	}
	if (fclose(fp)!=0)
	{
		printf("# Error in closing library file \"%s\". \n",libFile);
	}
	else printf("# Successfully closed library file \"%s\". \n# %d sections has been readed.\n",libFile,tot_students);
	return tot_students; //all the students/sections that've been readed from the file;
}

void qsort_stuData(int l, int r, int type=-1)
{
	int x=l,y=r,mid;
	char mid_str[128];
	stuInfo _temp;
	//type: idnum: -1, stuid -2, name -3, gender -4, course tag 
	switch(type) //set mid value
	{
		//case -4: strcpy(mid_str,stuData[(l+r)/2].Gender); break;
		//case -3: strcpy(mid_str,stuData[(l+r)/2].Name); break;
		case -2: mid = stuData[(l+r)/2].StuID_int; break;
		case -1: mid = stuData[(l+r)/2].identify_num; break;
		case 0: case 1: case 2: case 3:
			mid = stuData[(l+r)/2].scores[type]; break;
	}
	while (x<=y)
	{
		switch(type)
		{
			/*case -4: 
				while (strcmp(stuData[x].Gender,mid_str)<=0) x++;
				while (strcmp(stuData[y].Gender,mid_str)>=0) y--;
				break;
			case -3:
				while (strcmp(stuData[x].Name,mid_str)<=0) x++;
				while (strcmp(stuData[y].Name,mid_str)>=0) y--;
				break;*/
			case -2:
				while (stuData[x].StuID_int < mid) x++;
				while (stuData[y].StuID_int > mid) y--;
				break;
			case -1:
				while (stuData[x].identify_num < mid) x++;
				while (stuData[y].identify_num > mid) y--;
				break;
			case 0: case 1: case 2: case 3: //descending order
				while (stuData[x].scores[type] > mid) x++;
				while (stuData[y].scores[type] < mid) y--;
				break;
		}
		if (x<=y)
		{
			_temp = stuData[y];
			stuData[y] = stuData[x];
			stuData[x] = _temp;
			x++;
			y--;
		}
	}
	if(l<y) qsort_stuData(l,y,type);
	if(x<r) qsort_stuData(x,r,type);
}

int elimination(int k)
{
	int tot_students=k;
	for (int i=k-1; i>=0; i--)
	{
		for (int j=i-1; j>=0; j--)
		{
			if (stuData[j].identify_num==stuData[i].identify_num)
			{
				stuData[j].identify_num=DEL_IDENTIFY_NUM;
				tot_students--;
			}
		}
	}
	qsort_stuData(0,k-1);
	return tot_students;
}
void save_stuData(const char *libFile = "stulib1.txt")
{
	FILE* fp = fopen(libFile,"w");
	if (fp==NULL)
	{
		printf("Can't open library file \"%s\". \n",libFile);
		return ;
	}
	else printf("# Successfully opened library file \"%s\". \n",libFile);

	int k=0;
	int new_identify_num = 1;
	while (stuData[k].identify_num!=DEL_IDENTIFY_NUM && stuData[k].identify_num != 0)
	{
		fprintf(fp,"[%05d]\n",new_identify_num++); // assign a new identify num when saving the lib;  
		fprintf(fp,"ID = \"%s\"; \n",stuData[k].StuID);
		fprintf(fp,"Name = \"%s\"; \n",stuData[k].Name);
		fprintf(fp,"Gender = \"%s\"; \n",stuData[k].Gender);
		fprintf(fp,"Tel = \"%s\"; \n",stuData[k].TelNum);
		for (int i=0;i<COURSES_NUM;i++)
			fprintf(fp,"%s = %d; \n",courses_tag[i],stuData[k].scores[i]);
		//fprintf(fp,"Chn = %d; \n",stuData[k].scores[0]);
		//fprintf(fp,"Math = %d; \n",stuData[k].scores[1]);
		//fprintf(fp,"Eng = %d; \n",stuData[k].scores[2]);
		//printf("- %d students' information has been saved;\n",k+1); 
		printf("\n");
		k++;
	}
	if(fclose(fp)!=0) printf("Unable to close lib file \"%s\". \n",libFile);
	printf("# %d students' data has been saved to \"%s\". \n",k-1+1,libFile);
}
int getline(char* dstStr)
{
	char c;
	int k=0;
	scanf("%c",&c);
	while(c!='\n') 
	{
		*dstStr++ = c;
		k++;
		scanf("%c",&c);
	}
	*dstStr = '\0';
	return k;
}

int add_student()
{
	int i=0;
	//find a place at the end
	while(stuData[i].identify_num!=DEL_IDENTIFY_NUM && stuData[i].identify_num != 0) i++;
	printf("# After filling in these information, a new student will be added:\n");
	printf("  Student ID : ");
	getline(stuData[i].StuID); trim(stuData[i].StuID,stuData[i].StuID);
	printf("  Name       : ");
	getline(stuData[i].Name); trim(stuData[i].Name,stuData[i].Name);
	printf("  Gender     : ");
	getline(stuData[i].Gender); trim(stuData[i].Gender,stuData[i].Gender);
	printf("  Tel Number : ");
	getline(stuData[i].TelNum); trim(stuData[i].TelNum,stuData[i].TelNum);

	stuData[i].identify_num = stuData[abs(i-1)].identify_num + 1;
	stuData[i].StuID_int = atoi(stuData[i].StuID);
	printf("# "); view_student(i);
	printf("# Student added successfully! \n");
	return i+1; //now students;
}
void del_student(int i)
{
	int k=0;
	while (stuData[k].identify_num!=DEL_IDENTIFY_NUM && stuData[k].identify_num != 0) k++;
	view_student(i); //
	stuData[i].identify_num=DEL_IDENTIFY_NUM;
	qsort_stuData(0,k-1);
	printf("# Student deleted successfully!\n");
	return ;
}

void view_scores(int i, int mode = 0 ) //0 for table, 1 for linear
{
	printf("# %-8s %-16s ",stuData[i].StuID,stuData[i].Name);
	if (mode == -1) {printf("\n"); return;}
	if (mode == 0)
	{
		printf ("\n");
		for (int j=0;j < COURSES_NUM ;j++)
		{
			printf("- %-8s: %3d \n",courses_tag[j],stuData[i].scores[j]);
		}
	}
	/*if (mode==1)
	{
		for (int j=0;j < COURSES_NUM ;j++) printf("%d ",stuData[i].scores[j]);
		printf("\n");
	}*/
}
void view_all_students()
{
	printf("# Viewing all students' information: \n");
	int i=0;
	while(stuData[i].identify_num!=DEL_IDENTIFY_NUM && stuData[i].identify_num != 0)
	{
		view_student(i);
		i++;
	}
	printf("# Total: %d students\n",i);
}
int search_student(const char* keywords, int results_index[])
{
	int i=0,k=0;
	while(stuData[i].identify_num!=DEL_IDENTIFY_NUM && stuData[i].identify_num != 0)
	{
		//search keyword in stuID, name, tel;
		char* if_ID = strstr(stuData[i].StuID,keywords);
		char* if_Name = strstr (stuData[i].Name,keywords);
		char* if_Tel = strstr(stuData[i].TelNum,keywords);
		if( if_ID!= NULL || if_Name!= NULL ||if_Tel !=NULL) results_index[k++]=i;
		i++;
	}
	//view search results 
	printf("# %d results have been found! \n",k);
	for(int m=0;m<k;m++)
	{
		//printf ("%2d| ",m);
		view_student(results_index[m]);
	}
	return k; //return num. of the results
}
int search_score(int tgt_score,int results_index[], int mode = 0) //0 for below and 1 for above
{
	int i=0,k=0;
	while(stuData[i].identify_num!=DEL_IDENTIFY_NUM && stuData[i].identify_num != 0)
	{
		for(int j=0;j<COURSES_NUM;j++)
			if(stuData[i].scores[j]<tgt_score) 
			{
				results_index[k++]=i; 
				break;
			}
		i++;
	}
	printf("# %d results have been found! \n",k);
	for(int m=0;m<k;m++)
	{
		//printf ("%2d| ",m);
		view_student(results_index[m]);
	}
	return k;
}
void edit_info(int i)
{
	printf("* ");  view_student(i);
	printf("- Input something to replace previoius ones;\n");
	printf("- Type '#' to not modify certain items;\n");
	char input[128]; //set input buffer

	printf("  Stu. ID: %16s | ",stuData[i].StuID);
	getline(input); trim(input,input);
	if(input[0]!='#') { strcpy(stuData[i].StuID,input);
	stuData[i].StuID_int = atoi(stuData[i].StuID); }

	printf("  Name   : %16s | ",stuData[i].Name);
	getline(input); trim(input,input);
	if(input[0]!='#') strcpy(stuData[i].Name,input);

	printf("  Tel Num: %16s | ",stuData[i].TelNum);
	getline(input); trim(input,input);
	if(input[0]!='#') strcpy(stuData[i].TelNum,input);

	printf("  Gender : %16s | ",stuData[i].Gender);
	getline(input); trim(input,input);
	if(input[0]!='#') strcpy(stuData[i].Gender,input);

	printf("# Student's basic information has been updated!\n");
	printf("# "); view_student(i);
}
void edit_scores(int i)
{
	printf("- Input new scores to replace previoius ones;\n");
	printf("- Type '#' to not modify certain items;\n");
	//view_scores (i,1);
	char input[128]; //set input buffer
	view_scores(i,-1);
	for(int j=0;j<COURSES_NUM;j++)
	{
		printf("- %-8s: %3d | ",courses_tag[j],stuData[i].scores[j]);
		getline(input); trim(input,input);
		if(input[0]!='#') 
		{
			stuData[i].scores[j]=atoi(input);
		}
	}
	printf("# Student's scores has been updated!\n");
}
void edit_student_info()
{
	char input[128],choice='0';
	void (*p_function)(int);
	printf("# You're going to edit one student's information, \n");
	printf("- Enter 1: To edit one's basic Info. ; \n- Enter 2: To edit one's scores;\n");
	while (choice!='1'&&choice!='2')
	{
		getline(input); trim(input,input);
		choice = input[0]; 
		if (choice == '1') p_function=edit_info;
		else if (choice == '2') p_function=edit_scores;
		else continue;
	}
	printf("- Enter A: Edit by students current No. ;\n");
	printf("- Enter B: Search for students by a keyword, then edit. ;\n");
	printf("- Enter #: Exit editing. \n");

	getline(input); trim(input,input); //trim user's input
	choice=input[0];
	if (choice == '#') return;
	while (choice == 'A') //Mode 1: edit by No.;
	{
		printf("- Please enter his/her No.; ");
		printf("enter '#' to finish editing. \n" );
		getline(input);
		if (input[0]=='#') return;
		int i = atoi(input);
		if(i<=0) 
		{
			printf("Illegal input!\n");
			continue;
		}
		p_function(i-1);
		//return ;
	}
	if (choice =='B')
	{
		int results_index[128];
		printf("- Please input a keyword for searching : ");
		getline(input); trim(input,input);
		int k = search_student(input, results_index);

		while (input[0]!='#')
		{
			printf("- Enter the No. of a certain student to edit his/her info., \n");
			printf("- Enter '#' to stop editing. \n" );
			getline(input);
			if (input[0]=='#') return;
			int i = atoi(input);
			if(i<=0) 
			{
				printf("Illegal input!\n");
				continue;
			}
			p_function(i-1);
		}
		return;
	}
}
void view_help_text()
{
	const char* sep="---------------------------------------------------\n";
	printf("%s",sep);
	printf(" 0. Show Help text;\n");
	printf(" 1. Load students' data from \"stulib.txt\";\n");
	printf(" 2. Save current student's data to \"stulib.txt\";\n");
	printf(" 3. View all students;\n");
	printf(" 4. Search for students; \n");
	printf(" 5. Add a student;\n");
	printf(" 6. Edit a student;\n");
	printf(" 7. Delete a student;\n");
	printf(" 8. Sort by ... ;\n");
	printf("-1. Exit & Save. \n");
	printf("-2. Exit without saving. \n");
	printf("%s",sep);
}
void sort_by()
{
	int k=0;
	while (stuData[k].identify_num!=DEL_IDENTIFY_NUM && stuData[k].identify_num != 0) k++;
	// find tot student numbers; 
	//type: idnum: -1, stuid -2, name -3, gender -4, course tag 
	printf("# You're going to sort the students by: \n");
	printf("- 1. By student ID;\n");
	printf("- 2. By scores;\n");
	int choice = 0;
	char input[8];
	while (choice<=0 || choice > 2)
	{
		getline(input);
		choice = atoi(input);
	}
	if (choice == 1)
	{
		/*choice = 0;
		printf("- 1. By student ID;\n- 2. By name;\n- 3. By gender.\n");
		while (choice <= 0 || choice > 3) //ignore illegal input
		{
			getline(input);
			choice =  atoi (input);
		}
		//if (choice == 1) qsort_stuData(0,k-1, -2);
		//if (choice == 2) qsort_stuData(0,k-1, -3);
		//if (choice == 3) qsort_stuData(0,k-1, -4);
		//qsort_stuData(0,k-1, -choice-1);*/
		qsort_stuData(0,k-1,-2);
	}
	if (choice == 2)
	{
		choice = 0;
		show_courses_name();
		while (choice <= 0 || choice > COURSES_NUM ) //ignore illegal input
		{
			printf("- Type courses No. to sort by the scores of it.\n");
			getline(input);
			choice = atoi (input);
		}
		qsort_stuData(0,k-1,choice-1);
	}
	printf("# Sorted! \n");
	view_all_students();
}
void search_by(int *results)
{
	char input[128];
	printf("# Search for students by: \n");
	printf("- 1. A keyword; \n");
	printf("- 2. Students who failed; \n");
	int choice = 0;
	while (choice<=0 || choice > 2)
	{
		getline(input);
		choice = atoi(input);
	}
	if (choice == 1)
	{
		getline(input);
		trim(input,input);
		search_student(input,results);
	}
	if (choice == 2) 
	{
		search_score(60,results);
	}
}
int main()
{
	int tot_students=0;
	//tot_students = elimination(tot_students);
	int ch=0;
	int results[256];
	char input[128]={0};
	view_help_text();
	while (ch!=-1&&ch!=-2)
	{
		getline(input);
		ch = atoi(input);
		switch(ch)
		{
			case 0: view_help_text(); break;
			case 1: tot_students = load_stuData("stulib.txt"); break;
			case 2: save_stuData("stulib.txt"); break;
			case 3: view_all_students(); break;
			case 4: search_by(results); break;  
			case 5: tot_students = add_student(); break;
			case 6: edit_student_info(); break;
			case 7:
				printf("# Input the No. of the student to delete: ");
				getline(input);
				del_student( atoi(input) -1);
				break; 
			case 8: sort_by(); break;
			case -1: save_stuData(); break;
			case -2: break;
		}
		if (ch>0) printf("# Backed to the main menu. Enter 0 for HELP. \n");
	}
	return 0;
}
